﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;

namespace BingDesktopChanger
{

    delegate void NotifyStatusDelegate(object sender, StatusNotificationEventArgs e);


    internal class ImageInfo
    {
        public ImageInfo(string imageUrl)
        {
            this.ImageNameForUrl = imageUrl;
            if (imageUrl.Contains("_"))
                this.RealImageName = imageUrl.Split('_')[0].ToLowerInvariant();
            else
                this.RealImageName = imageUrl;
            this.ExistsInDataBase = false;
        }

        public string ImageNameForUrl { get; private set; }
        public string RealImageName { get; private set; }
        public bool ExistsInDataBase { get; set; }
    }

    internal class ImageReader
    {
        internal event NotifyStatusDelegate StatusChanged
        {
            add { this.notifyStatusEvent = value; }
            remove { this.notifyStatusEvent = null; }
        }

        protected void OnNotifyStatus(StatusCode statusCode)
        {
            if (this.notifyStatusEvent != null)
                this.notifyStatusEvent.Invoke(this, new StatusNotificationEventArgs(statusCode));
        }
        protected void OnNotifyStatus(DateTime targetDay)
        {
            if (this.notifyStatusEvent != null)
                this.notifyStatusEvent.Invoke(this, new StatusNotificationEventArgs(targetDay));
        }
        protected void OnNotifyStatus(int currentImageIndex, string currentImageName, int imagesCount)
        {
            if (this.notifyStatusEvent != null)
            {
                var e = new StatusNotificationEventArgs(StatusCode.GettingImage);
                e.CurrentImageIndex = currentImageIndex;
                e.CurrentImageName = currentImageName;
                e.ImagesCount = imagesCount;
                this.notifyStatusEvent.Invoke(this, e);
            }
        }


        public ImageReader(IImageManager manager)
        {
            this.manager = manager;
            this.client = new WebClient();
            this.client.UseDefaultCredentials = true;
        }

        #region Fields
        private NotifyStatusDelegate notifyStatusEvent;

        private IImageManager manager;
        private string targetKey;
        private WebClient client;
        private DateTime targetDay;

        private string lastPeriod = string.Empty;
        private string indexDocument = string.Empty;
        //private string[] dayImageList;
        private ImageInfo[] dayImageList;

        #endregion

        public void Upgrade(DateTime fromDate, DateTime toDate)
        {
            fromDate = new DateTime(Math.Max(fromDate.Ticks, new DateTime(2009, 05, 24).Ticks));
            toDate = new DateTime(Math.Max(Math.Min(DateTime.Today.Ticks, toDate.Ticks), fromDate.Ticks));

            this.SetTargetDate(fromDate);
            this.lastPeriod = string.Empty;
            while (this.targetDay <= toDate)
            {
                if (this.lastPeriod != this.targetDay.ToString("yyyyMM"))
                    this.GetMonthlyIndexDocument();

                this.AnalizeIndexDocument();
                if (this.dayImageList.Length > 0)
                    this.DownloadImages();

                this.OnNotifyStatus(this.targetDay);
                this.SetTargetDate(this.targetDay.AddDays(1));
            }

        }
        public bool ReadDayImages(DateTime targetDay)
        {
            this.SetTargetDate(targetDay);
            try
            {
                this.GetMonthlyIndexDocument();
                this.AnalizeIndexDocument();
                return this.DownloadImages();
            }
            catch (Exception ex)
            {
                Helper.LogException(ex);
            }
            return false;
        }

        private void GetMonthlyIndexDocument()
        {
            this.OnNotifyStatus(StatusCode.ReadingIndexDocument);
            this.lastPeriod = this.targetDay.ToString("yyyyMM");
            string indexUrl = string.Format(CommonConfig.Default.RootBaseUrl, this.targetDay.Month.ToString("00"), this.targetDay.Year);
            Stream documentStream = this.client.OpenRead(indexUrl);
            using (StreamReader reader = new StreamReader(documentStream))
                this.indexDocument = reader.ReadToEnd();
        }
        private void AnalizeIndexDocument()
        {
            this.OnNotifyStatus(StatusCode.AnalizingIndexDocument);

            Debug.Assert(this.indexDocument.Length > 0, "RSS Document Empty");
            List<ImageInfo> images = new List<ImageInfo>();

            string searchKey = string.Concat("<a href=\"", this.targetKey, "-");
            int patternPosition = this.indexDocument.IndexOf(searchKey);
            while (patternPosition > 0)
            {
                int startIndex = this.indexDocument.IndexOf("i=", patternPosition + 1);
                int endIndex = this.indexDocument.IndexOf("&", startIndex + 1);

                string imageUrl = this.indexDocument.Substring(startIndex + 2, (endIndex - startIndex - 2)).ToLower();
                ImageInfo item = new ImageInfo(imageUrl);
                if (!images.Exists(i => i.ImageNameForUrl.Contains(item.RealImageName)))
                {
                    item.ExistsInDataBase = this.manager.ExistsImage(item.RealImageName);
                    images.Add(item);
                }
                patternPosition = this.indexDocument.IndexOf(searchKey, endIndex + 1);
            }
            this.dayImageList = images.ToArray();
        }
        private bool DownloadImages()
        {
            int index = 0;
            foreach (var item in this.dayImageList)
                try
                {
                    this.OnNotifyStatus(++index, item.RealImageName, this.dayImageList.Length);
                    if (item.ExistsInDataBase)
                        this.manager.SaveImage(null, this.targetDay, item.RealImageName);
                    else
                    {
                        string imageUrl = string.Concat(CommonConfig.Default.ImageBaseUrl, item.ImageNameForUrl);
                        Stream imageStream = this.client.OpenRead(imageUrl);
                        this.manager.SaveImage(imageStream, this.targetDay, item.RealImageName);
                    }
                }
                catch (Exception ex)
                {
                    Helper.LogException(ex);
                    return false;
                }
            return true;
        }
        private void SetTargetDate(DateTime targetDay)
        {
            this.targetDay = targetDay;
            this.targetKey = string.Concat("#", targetDay.ToString("yyyyMMdd"));
        }
    }
}
